﻿/*----------------------------------------------------------*
 *                       Ball Class                         *
 *                                                          *
 *          Created By: Matthew Hoyes & Ronald Ram          *
 *                                                          *
 *      This class will eventually serve as an Abstract     *
 *      Class which all other types/colors of balls         *
 *      will inherit from, overloading the base methods.    *
 *----------------------------------------------------------*/

#region USING STATEMENTS

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics; // For Texture2D
using Microsoft.Xna.Framework;          // For Vector2

#endregion USING STATEMENTS

namespace BrickBreaker.Core
{
    #region ENUMS
    //
    public enum BallState
    {
        IDLE,   //IDLE = Ball is sitting on Paddle, Ball NOT Launched
        ACTIVE, //ACTIVE = Ball has been LAUNCHED, game in-progress
        DEAD    //DEAD = Player failed to hit the Ball, 1 Life Lost
    };

    public enum BallType
    {
        NONE,       //NONE = Ball is sitting on Paddle, NOT Launched
        DEFAULT,    //DEFAULT = the normal ball used in game
        FIRE_BALL,  //FIRE_BALL = can break bricks that takes 2+ hits to break 
        SPLIT_BALL, //SPLIT_BALL = When single ball splits into three(3) balls
        ACID_BALL   //ACID_BALL = Breaks thru 'bricks|obstacles' in its direction. Only reflects off walls & paddle 
        //ETC
    };
    //
    #endregion ENUMS


    public class BBBall : BBSingleSprite
    {
        #region DATA_MEMBERS
        //
        private BallState ballState;                //V0--: Track current Ball State
        private BallType ballType;                  //V0--: Type of ball used in game, Update Texture when BallType changes

        private Vector2 resetPosition;              //V0--: IF BallState=DEAD, THEN Ball.Position=resetPosition

        private const float SPEED_MIN = 3.0f;       //V0--: Min Speed of Ball
        private const float SPEED_MAX = 6.0f;       //V0--: Max Speed of Ball
        private const float SPEED_DEFAULT = 4.0f;   //V0--: Normal Speed of Ball

        private BBBallDirection launchDirection;    //V0--: Launch direction for Ball on Game Start

        private bool launched;                      //V0--: Has the ball launched from the paddle?
        private bool isPrimary;
        //       
        #endregion DATA_MEMBERS


        #region OBJECT CONSTRUCTION
        //
        /// <summary>
        /// Creates a new ball to be used. Position is set to the specified
        /// location.
        /// </summary>
        /// <param name="texture">The ball texture</param>
        /// <param name="position">The position of the ball</param>
        //M001: Default Constructor
        public BBBall(Texture2D texture, Vector2 position): base(texture, position)
        {
            Init();
        }

        /// <summary>
        /// Initializes the ball's properties.
        /// </summary>
        private void Init()
        {
            base.size = new Vector2(texture.Width, texture.Height);//new Vector2(12.0f, 12.0f);
            base.speed = new Vector2(SPEED_DEFAULT);

            launchDirection = new BBBallDirection( BBGlobalConfig.bbball_launchDirection);

            //Launch at 45 degrees for now
            base.direction = new Vector2(0.71f, -0.71f); //(1f, -1f);
            
            this.ballState = BallState.IDLE;
            this.ballType = BallType.DEFAULT;
            this.resetPosition = base.position;

            this.launched = false;
            this.isPrimary = false;
        }
        //
        #endregion OBJECT CONSTRUCTION


        #region PROPERTIES
        //
        public bool CanLaunch
        {
            get { return launched; }
            set { launched = value; }
        }

        public BallState State
        {
            get { return this.ballState; }
            set { this.ballState = value; }
        }

        public BallType Type
        {
            get { return this.ballType; }
            set { this.ballType = value; }
        }

        public bool IsPrimaryBall
        {
            get { return isPrimary; }
            set { isPrimary = value; }
        }

        public override Vector2 Speed
        {
            get { return base.speed; }
            set
            {
            // This was causing the ball to speed up, even when slow pwrup was collected
            // ----------------------------------------------------
                if (base.speed.X <= SPEED_MIN)
                {
                    base.speed = new Vector2(SPEED_MIN, SPEED_MIN);
                }
                if (base.speed.X >= SPEED_MAX)
                {
                    base.speed = new Vector2(SPEED_MAX, SPEED_MAX);
                }
                else
                { 
                    base.speed = value; 
                }
            }
        }
        //
        #endregion PROPERTIES

        
        #region OVERRIDE_METHODS
        //
        /// <summary>
        /// Resets the ball and all it's properties.
        /// </summary>
        public override void Reset()
        {
            base.position = this.resetPosition;
            base.speed = new Vector2(SPEED_DEFAULT);

            //Launch at 45 degrees
            base.direction = Vector2.Zero;//new Vector2(0.71f, -0.71f);
            
            this.ballState = BallState.IDLE;
            this.ballType = BallType.DEFAULT;

            this.launched = false;
            launchDirection.Reset();
        }

        /// <summary>
        /// Move method should be called in an Update method. Serves to
        /// move the ball on screen.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        //M0--: Update Ball Movement
        public override void Move(GameTime gameTime)
        {
            //Update ball launch direction 
            if (this.ballState == BallState.IDLE)
            {
                launchDirection.Update(gameTime, base.position);
                launchDirection.Position = new Vector2(base.position.X + (base.size.X / 2), base.position.Y + (base.size.Y + 2));
            }
            
            if(this.ballState == BallState.IDLE && this.launched)
            {
                if (isPrimary)
                {
                    base.direction = launchDirection.Direction;
                }
                this.ballState = BallState.ACTIVE;
            }
            
            //Update Ball Position, if BallState = ACTIVE|DEAD
            if (this.ballState != BallState.IDLE)
            {
                BoundaryCheck();
                /*IF ball is moving too fast, use this code: 
                position += speed * direction * (float)gameTime.ElapsedGameTime.TotalSeconds;
                */
                position += (speed * direction);
            }
        }

        /// <summary>
        /// Draws the ball.  Call this in a spritebatch.Begin()/End() block.
        /// </summary>
        /// <param name="spriteBatch">Your spritebatch.  Make sure you have already called begin().</param>
        //M0--: Displays the ball on screen
        public override void Draw(SpriteBatch spriteBatch)
        {
            if (this.ballState == BallState.IDLE)
            {
                launchDirection.Draw(spriteBatch);
            }
            else if (this.ballState == BallState.ACTIVE)
            {
                spriteBatch.Draw(texture, position, Color.White);
            }

            //Dsiplay Stats for Debugging 
            if (BBGlobalConfig.DisplayDebug == true)
            {
                spriteBatch.DrawString(BBGlobalConfig.font, "Ball Speed( " + base.speed.X.ToString() + ", " + base.speed.Y.ToString() + ")", new Vector2(50, 80), Color.Red);
                spriteBatch.DrawString(BBGlobalConfig.font, "Ball State( " + this.State + " )", new Vector2(50, 200), Color.Red); 
                spriteBatch.DrawString(BBGlobalConfig.font, "Direction( " + base.direction.X.ToString() + ", " + base.direction.Y.ToString() + ")", new Vector2(50, 470), Color.Red);
            }
        }

        /// <summary>
        /// Changes the direction of the ball to reflect in the direction specified
        /// </summary>
        /// <param name="newDirection">The new direction the ball should travel.</param>
        //M0--: Change movement Direction of ball
        public void Reflect(Vector2 newDirection)
        {
            this.direction = newDirection;
            DirectionNormalize();
        }
        //
        #endregion OVERRIDE_METHODS

        
        #region PUBLIC_METHODS
        //

        
        /// <summary>
        /// Checks whether the ball has collided with an object.
        /// Axis Aligned Bounding Box collision check.
        /// </summary>
        /// <param name="theSprite">The object being checked for collision.</param>
        //M0--: First Pass, Box Collision Check
        public bool hasCollided(BBSingleSprite theSprite)
        {
            if(this.Intersects(theSprite.getRectBounds()))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public Texture2D DecideTexture(int index)
        {
            Texture2D t;

            if (index < 1)
            {
                index = 1;
            }

            if (index > 8) // the number of different balls
            {
                index = 8;
            }

            switch (index)
            {
                case 1:
                    t = BBGlobalConfig.bbball_black;
                    break;

                case 2:
                    t = BBGlobalConfig.bbball_blue;
                    break;

                case 3:
                    t = BBGlobalConfig.bbball_blurple;
                    break;

                case 4:
                    t = BBGlobalConfig.bbball_green;
                    break;

                case 5:
                    t = BBGlobalConfig.bbball_greenblue;
                    break;

                case 6:
                    t = BBGlobalConfig.bbball_purple;
                    break;

                case 7:
                    t = BBGlobalConfig.bbball_red;
                    break;

                case 8:
                    t = BBGlobalConfig.bbball_yellow;
                    break;

                default:
                    t = null;
                    break;
            }

            return t;
        }
        //
        #endregion PUBLIC_METHODS

    }//end class
}//end namespace
